#include "ECS/PEntity.h"

#include "PiccoloGeometryUtil.h"

#include "Graphics/PiccoloVKPipeline.h"
#include "Graphics/PiccoloVKRenderPass.h"
#include "Graphics/PiccoloVKCommandBuffer.h"

#include "PiccoloEntryPoint.h"
#include "Render/PiccoloRenderTarget.h"
#include "Render/PiccoloMesh.h"
#include "Render/PiccoloRenderer.h"

#include "ECS/System/PBaseMaterialSystem.h"

namespace Piccolo
{
    class SandBoxApp : public PiccoloApplication
    {
    protected:
        void OnConfiguration(AppSettings *appSettings) override
        {
            appSettings->width = 1360;
            appSettings->height = 768;
            appSettings->title = "SandBox";
        }

        void OnInit() override
        {
            PiccoloRenderContext* renderContext = GetAppContext()->renderContext;
            PiccoloVKDevice* device = renderContext->GetDevice();
            PiccoloVKSwapchain* swapchain = renderContext->GetSwapchain();

            // 创建颜色附件和深度附件
            std::vector<Attachment> attachments =
            {
                {
                    .format = swapchain->GetSurfaceInfo().surfaceFormat.format,
                    .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
                    .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
                    .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
                    .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
                    .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
                    .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
                    .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
                },
                {
                    .format = device->GetSettings().depthFormat,
                    .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
                    .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
                    .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
                    .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
                    .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
                    .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
                    .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT
                }
            };

            std::vector<RenderSubPass> subPasses =
            {
                {
                    .colorAttachments = { 0 },
                    .depthStencilAttachments = { 1 },
                    .sampleCount = VK_SAMPLE_COUNT_4_BIT
                }
            };
            mRenderPass = std::make_shared<PiccoloVKRenderPass>(device, attachments, subPasses);
            mRenderTarget = std::make_shared<PiccoloRenderTarget>(mRenderPass.get());
            mRenderTarget->SetColorClearValue({ 0.1f, 0.2f, 0.3f, 1.f });
            mRenderTarget->SetDepthStencilClearValue({ 1, 0 });
            // 给这个render target添加一个材质系统
            mRenderTarget->AddMaterialSystem<PBaseMaterialSystem>();

            mRenderer = std::make_shared<PiccoloRenderer>();
            mCommandBuffers = device->GetDefaultCommandPool()->AllocateCommandBuffer(swapchain->GetImages().size());

            std::vector<PiccoloVertex> vertices;
            std::vector<uint32_t> indices;
            PiccoloGeometryUtil::CreateCube(-0.3f, 0.3f, -0.3f, 0.3f, -0.3f, 0.3f, vertices, indices);
            mCubeMesh = std::make_shared<PiccoloMesh>(vertices, indices);
        }

        void OnSceneInit(PScene *scene) override
        {
            {
                PEntity* cube = scene->CreateEntity("Cube 0");
                auto& materialComponent = cube->AddComponent<PBaseMaterialComponent>();
                materialComponent.colorType = COLOR_TYPE_TEXCOORD;

                auto& meshComponent = cube->AddComponent<PMeshComponent>();
                meshComponent.mMesh = mCubeMesh.get();

                auto& transformComponent = cube->GetComponent<PTransformComponent>();
                transformComponent.scale = { 0.25f, 0.25f, 0.25f };
                transformComponent.position = { 1.f, 0.f, -1.f };
                transformComponent.rotation = { 17.f, 50.f, 0.f };
            }

            {
                PEntity* cube = scene->CreateEntity("Cube 1");
                auto& materialComponent = cube->AddComponent<PBaseMaterialComponent>();
                materialComponent.colorType = COLOR_TYPE_NORMAL;

                auto& meshComponent = cube->AddComponent<PMeshComponent>();
                meshComponent.mMesh = mCubeMesh.get();

                auto& transformComponent = cube->GetComponent<PTransformComponent>();
                transformComponent.scale = { 1.25f, 1.25f, 1.25f };
                transformComponent.position = { -1.f, 0.f, -1.f };
                transformComponent.rotation = { 0.f, 50.f, 0.f };
            }
        }

        void OnSceneDestroy(PScene *scene) override
        {

        }

        void OnUpdate(float deltaTime) override
        {

        }

        void OnRender() override
        {
            PiccoloRenderContext* renderContext = GetAppContext()->renderContext;
            PiccoloVKDevice* device = renderContext->GetDevice();
            PiccoloVKSwapchain* swapchain = renderContext->GetSwapchain();

            int32_t imageIndex;
            if (mRenderer->Begin(&imageIndex))
            {
                mRenderTarget->SetExtent({ swapchain->GetWidth(), swapchain->GetHeight() });
            }
            VkCommandBuffer cmdBuffer = mCommandBuffers[imageIndex];
            PiccoloVKCommandPool::BeginCommandBuffer(cmdBuffer);

            mRenderTarget->Begin(cmdBuffer);
            mRenderTarget->RenderMaterialSystemList(cmdBuffer);
            mRenderTarget->End(cmdBuffer);

            PiccoloVKCommandPool::EndCommandBuffer(cmdBuffer);
            if (mRenderer->End(imageIndex, { cmdBuffer }))
            {
                mRenderTarget->SetExtent({ swapchain->GetWidth(), swapchain->GetHeight() });
            }
        }

        void OnDestroy() override
        {
            PiccoloRenderContext* renderContext = GetAppContext()->renderContext;
            PiccoloVKDevice* device = renderContext->GetDevice();
            vkDeviceWaitIdle(device->GetLogicDevice());
            mCubeMesh.reset();
            mCommandBuffers.clear();
            mRenderTarget.reset();
            mRenderPass.reset();
            mRenderer.reset();
        }

    private:
        std::shared_ptr<PiccoloVKRenderPass> mRenderPass;
        std::shared_ptr<PiccoloRenderTarget> mRenderTarget;
        std::shared_ptr<PiccoloRenderer> mRenderer;

        std::vector<VkCommandBuffer> mCommandBuffers;
        std::shared_ptr<PiccoloMesh> mCubeMesh;
    };
}

Piccolo::PiccoloApplication* CreateApplicationEntryPoint()
{
    return new Piccolo::SandBoxApp();
}